﻿namespace AY38910
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    public class AYFileReader : FileReader
    {
        #region Public Methods and Operators

        const int PortMask = 0xc002;

        public AyFile Read(string fileName)
        {
            using (var stream = File.OpenRead(fileName))
            {
                stream.Position = 0;

                var ayFile = new AyFile
                                 {
                                     FileHeader = stream.ReadString(4),
                                     FileType = stream.ReadString(4),
                                     FileVersion = (byte)stream.ReadByte(),
                                     PlayerVersion = (byte)stream.ReadByte(),
                                     PSpecialPlayer = stream.GetAddress(),
                                     PAuthor = stream.GetAddress(),
                                     PMisc = stream.GetAddress(),
                                     NumOfSongs = (byte)stream.ReadByte(),
                                     FirstSong = (byte)stream.ReadByte(),
                                     PSongStructure = stream.GetAddress()
                                 };

                // Author
                stream.Seek(ayFile.PAuthor, SeekOrigin.Begin);
                ayFile.Author = stream.ReadString();

                // Misc
                stream.Seek(ayFile.PMisc, SeekOrigin.Begin);
                ayFile.Misc = stream.ReadString();

                // Song structures
                ayFile.Songs = new SongStructure[ayFile.NumOfSongs + 1];
                stream.Seek(ayFile.PSongStructure, SeekOrigin.Begin);
                for (int i = 0; i < ayFile.NumOfSongs + 1; i++)
                {
                    ayFile.Songs[i] = new SongStructure { PSongName = stream.GetAddress(), PSongData = stream.GetAddress() };
                }

                // Song names
                for (int i = 0; i < ayFile.NumOfSongs + 1; i++)
                {
                    stream.Seek(ayFile.Songs[i].PSongName, SeekOrigin.Begin);
                    ayFile.Songs[i].SongName = stream.ReadString().Trim();
                }

                // Song data
                if (ayFile.FileType == "EMUL")
                {
                    var songDatas = new SongData[ayFile.NumOfSongs + 1];
                    for (int i = 0; i < ayFile.NumOfSongs + 1; i++)
                    {
                        stream.Seek(ayFile.Songs[i].PSongData, SeekOrigin.Begin);

                        var songData = new SongData
                                           {
                                               ChanA = (byte)stream.ReadByte(),
                                               ChanB = (byte)stream.ReadByte(),
                                               ChanC = (byte)stream.ReadByte(),
                                               Noise = (byte)stream.ReadByte(),
                                               SongLength = stream.ReadUInt16(true),
                                               FadeLength = stream.ReadUInt16(true),
                                               HiReg = (byte)stream.ReadByte(),
                                               LoReg = (byte)stream.ReadByte(),
                                               PPoints = stream.GetAddress(),
                                               PAddresses = stream.GetAddress()
                                           };

                        songDatas[i] = songData;
                    }

                    ayFile.SongDatas = songDatas;
                }

                // Points
                var pointsList = new List<Points>();
                foreach (var songData in ayFile.SongDatas)
                {
                    stream.Seek(songData.PPoints, SeekOrigin.Begin);
                    var points = new Points
                                     {
                                         Stack = stream.ReadUInt16(true),
                                         Init = stream.ReadUInt16(true),
                                         Inter = stream.ReadUInt16(true)
                                     };
                    pointsList.Add(points);
                }

                ayFile.Points = pointsList;

                // Block Data
                ayFile.Blocks = new List<BlockData>[ayFile.NumOfSongs + 1];
                for (int i = 0; i < ayFile.NumOfSongs + 1; i++)
                {
                    stream.Seek(ayFile.SongDatas[i].PAddresses, SeekOrigin.Begin);

                    var blockDatas = new List<BlockData>();
                    while (true)
                    {
                        var blockData = new BlockData { Address = stream.ReadUInt16(true) };
                        if (blockData.Address == 0)
                        {
                            break;
                        }

                        blockData.Length = stream.ReadUInt16(true);
                        blockData.Offset = stream.GetAddress();
                        blockDatas.Add(blockData);

                        // Just in case
                        if (stream.Position >= stream.Length)
                        {
                            break;
                        }
                    }

                    ayFile.Blocks[i] = blockDatas;
                }

                ayFile.Song = new List<Tuple<Registers, byte>>();
                foreach (var blockData in ayFile.Blocks[0])
                {
                    stream.Seek(blockData.Offset, SeekOrigin.Begin);

                    
                    var buffer = new byte[blockData.Length];
                    stream.Read(buffer, 0, blockData.Length);
                    var memoryStream = new MemoryStream(buffer);
                }

                return ayFile;
            }
        }



        #endregion

        #region Methods

        private AY8910 WRPORT(ushort addr, byte value) // cpu write port   
        {
            var result = new AY8910(44100, 71680);
            if ((addr & 0xC0FF) == 0xC0FD) // AY-8910 port #FFFD (reg#)
            {
                result.ADDR_REG = value;
            }
            if ((addr & 0xC0FF) == 0x80FD) // AY-8910 port #BFFD (data)
            {
                // result.process(GetFrameTact());
                result.SetDATA_REG(value);
            }

            return result;
        }

        #endregion
    }

    public class BlockData
    {
        #region Public Properties

        public ushort Address { get; set; }

        public ushort Length { get; set; }

        public short Offset { get; set; }

        #endregion
    }

    public struct Points
    {
        #region Public Properties

        public ushort Init { get; set; }

        public ushort Inter { get; set; }

        public ushort Stack { get; set; }

        #endregion
    }

    public struct SongData
    {
        #region Public Properties

        public byte ChanA { get; set; }

        public byte ChanB { get; set; }

        public byte ChanC { get; set; }

        public ushort FadeLength { get; set; }

        public byte HiReg { get; set; }

        public byte LoReg { get; set; }

        public byte Noise { get; set; }

        public short PAddresses { get; set; }

        public short PPoints { get; set; }

        public ushort SongLength { get; set; }

        #endregion
    }

    public class SongStructure
    {
        #region Public Properties

        public short PSongData { get; set; }

        public short PSongName { get; set; }

        public string SongName { get; set; }

        #endregion
    }

    public class AyFile
    {
        #region Public Properties

        public string Author { get; set; }

        public List<BlockData>[] Blocks { get; set; }

        public string FileHeader { get; set; }

        public string FileType { get; set; }

        public byte FileVersion { get; set; }

        public byte FirstSong { get; set; }

        public string Misc { get; set; }

        public byte NumOfSongs { get; set; }

        public short PAuthor { get; set; }

        public short PMisc { get; set; }

        public short PSongStructure { get; set; }

        public short PSpecialPlayer { get; set; }

        public byte PlayerVersion { get; set; }

        public List<Points> Points { get; set; }

        public IList<Tuple<Registers, byte>> Song { get; set; }

        public SongData[] SongDatas { get; set; }

        public SongStructure[] Songs { get; set; }

        #endregion
    }
}